[org 0x1000]

db 0x55, 0xaa

mov si, loading
call print

detect_memory:
    xor ebx, ebx
    xor eax, eax
    mov es, ax
    mov edi, ards_buffer
    mov edx, 0x534d4150
.next:
    mov eax, 0xe820
    mov ecx, 20
    int 0x15
    jc error
    add di, cx
    inc dword [ards_count]
    cmp ebx, 0
    jnz .next

    mov si, detecting
    call print

jmp prepare_protected_mode

print:
    mov ah, 0x0e
.next:
    mov al, [si]
    cmp al, 0
    jz .done
    int 0x10
    inc si
    jmp .next
.done:
    ret

error:
    mov si, message
    call print
    hlt ; Shut down the CPU
    jmp $

prepare_protected_mode:
    cli ; 关中断
    ; 开启A20线
    in al, 0x92
    or al, 0b10
    out 0x92, al
    ; 加载gdt
    lgdt [gdt32_ptr]
    ; 开启保护模式
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    ; 用jmp来刷新gdt缓存
    jmp code_selector:protected_mode

[bits 32]
protected_mode:
    ; 初始化段寄存器
    mov ax, data_selector
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ; 初始化栈顶指针
    mov eax, 0x10000
    mov esp, eax
    ; 加载kernel
    mov edi, eax    ; 读数据存放的地址
    mov ecx, 10     ; 起始扇区地址
    mov bl, 200     ; 读扇区数量
    call read_disk
    jmp dword code_selector:prepare_long_mode
    ; 表示出错
    ud2

prepare_long_mode:
    mov eax, 0x80000000
    cpuid   ; 判断扩展功能号是否超过0x80000000
    cmp eax, 0x80000001
    setnb al    ; 将al置为1,eax=0x80000001
    cpuid   ; eax返回CPU支持的最大扩展功能号
    ; edx返回处理器信息，第29位指示是否支持IA-32e
    bt edx, 29  ; 位操作指令，将第29位复制进CF位
    setb al     ; 将CF位复制到寄存器
    cmp al, 0
    jz .not_support
.prepare_long_mode_done:
    jmp long_mode
.not_support:
    hlt ; Shut down the CPU
    jmp $

long_mode:
    ; 初始化临时页表到0x80000的位置
    ; 初始化了8M物理内存，每页2M
    mov	dword [0x80000], 0x81007
	mov	dword [0x80800], 0x81007		
	mov	dword [0x81000], 0x82007
	mov	dword [0x82000], 0x000083
	mov	dword [0x82008], 0x200083
    mov	dword [0x82010], 0x400083
    mov	dword [0x82018], 0x600083
    ; 使能物理地址扩展(PAE)
	mov	eax, cr4
	bts	eax, 5  ; 将第5位置1
	mov	cr4, eax
    ; 加载页表
	mov	eax, 0x80000
	mov	cr3, eax
    ; 使能long mode(LME)
	mov	ecx, 0x0C0000080  ; IA32_EFER MSR ADDR
	rdmsr       ; 读IA32_EFER MSR
	bts	eax, 8  ; 将第8位置1
	wrmsr       ; 写IA32_EFER MSR
    ; 打开分页机制(PG)
	mov	eax, cr0
    bts	eax, 0
	bts	eax, 31
	mov	cr0, eax
    ; load 64-bit gdt
    lgdt [gdt64_ptr]
    ; 初始化数据段寄存器
    mov ax, data_selector
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ; 初始化代码段寄存器，并跳转到内核_start处
    mov edi, ards_count ; ARDS的起始地址
    jmp dword code_selector:0x10000
    ud2 ; 表示出错

read_disk:
    mov dx, 0x1F2
    mov al, bl
    out dx, al  ; 读扇区数量

    inc dx  ; 0x1F3
    mov al, cl
    out dx, al  ; 起始扇区 0~7位

    inc dx  ; 0x1F4
    shr ecx, 8
    mov al, cl
    out dx, al  ; 起始扇区 8~15位
    
    inc dx  ; 0x1F5
    shr ecx, 8
    mov al, cl
    out dx, al  ; 起始扇区 16~23位
    
    inc dx  ; 0x1F6
    shr ecx, 8
    and cl, 0b1111
    mov al, 0b11100000
    or al, cl
    out dx, al  ; LBA, 主盘, 起始扇区 27~24位
    
    inc dx  ; 0x1F7
    mov al, 0x20    ; 读硬盘
    out dx, al
    
    xor ecx, ecx; 清空ecx
    mov cl, bl  ; 得到读扇区数量

.read:
    call .waits
    push cx
    call .reads
    pop cx
    loop .read
    ret

.reads:
    mov dx, 0x1F0
    mov cx, 256
.readw:
    in ax, dx
    jmp $+2
    jmp $+2
    jmp $+2 ; 延迟一会儿
    mov [edi], ax
    add edi, 2
    loop .readw
    ret

.waits:
    mov dx, 0x1F7
.check:
    in al, dx
    jmp $+2
    jmp $+2
    jmp $+2 ; 延迟一会儿
    and al, 0b00001000
    cmp al, 0b00001000
    jnz .check
    ret

loading:
    db "Loading Yonex...", 13, 10, 0;

detecting:
    db "Detecting Memory Finished", 13, 10, 0;

message:
    db "Loading Error!!!", 13, 10, 0;

memory_base equ 0
memory_limit equ 0xfffff

    align 4    ; 对齐4字节
gdt32_base:
    dd 0, 0    ; NULL描述符
gdt32_code:
    dw (memory_limit & 0xffff)
    dw (memory_base & 0xffff)
    db ((memory_base >> 16) & 0xff)
    db 0b1001_1010
    db (0b1100_0000 | ((memory_limit) >> 16) & 0xf)
    db ((memory_base >> 24) & 0xff)
gdt32_data:
    dw (memory_limit & 0xffff)
    dw (memory_base & 0xffff)
    db ((memory_base >> 16) & 0xff)
    db 0b1001_0010
    db (0b1100_0000 | ((memory_limit) >> 16) & 0xf)
    db ((memory_base >> 24) & 0xff)
gdt32_end:

gdt32_ptr:
    dw ((gdt32_end - gdt32_base) - 1)
    dd gdt32_base

gdt64_base:
    dd 0, 0    ; NULL描述符
gdt64_code:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 0b1001_1010
    db 0b0010_0000
    db 0x00
gdt64_data:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 0b1001_0010
    db 0b0010_0000
    db 0x00
gdt64_end:

gdt64_ptr:
    dw ((gdt64_end - gdt64_base) - 1)
    dq gdt64_base

code_selector equ ((1 << 3) | 0b000)
data_selector equ ((2 << 3) | 0b000)

ards_count:
    dd 0
ards_buffer:
